package models.repository;

import models.entities.OrderEntity;
import models.entities.PaginatedList;
import models.entities.UserEntity;
import models.entities.UserInformation;
import models.exceptions.*;
import models.helpers.DBSession;
import models.repository.common.UserRepository;
import mybatis.mappers.OrderMapper;
import mybatis.mappers.UserMapper;
import org.apache.ibatis.session.SqlSession;
import play.mvc.Scope;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;

@Deprecated
public class UserRepositorySQL implements UserRepository {
    public void mergeShoppingCarts(String username, Scope.Session session) throws UpdateEntityException, CreateEntityException, GetEntityException, IOException {
        UserEntity authUser = getUser(username);
        UserEntity anonymousUser = getAnonymousUser(session.getId());
        mergeShoppingCarts(authUser.id, anonymousUser.id);
    }

    public boolean validate(String email, String password) throws GetEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            UserEntity user = userMapper.authenticate(email, password);
            return user != null;
        } catch (Exception ex) {
            throw new GetEntityException(MessageFormat.format("Cannot validate user with email: '{0}' and password: {1}; Original message: {2}", email, password, ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public void register(UserEntity user) throws CreateEntityException, UserExistsException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            UserEntity registeredUser = userMapper.getUser(user.email);
            if (registeredUser != null)
                throw new UserExistsException("User with this email already exists");

            userMapper.addAnonymousUser(user);
            userMapper.register(user);
            dbSession.commit();
        } catch (Exception ex) {
            throw new CreateEntityException(MessageFormat.format("Cannot create user with email: {0} and password: {1}; Original message: {2}", user.email, user.password, ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public UserEntity getUser(String email) throws GetEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            UserEntity registeredUser = userMapper.getUser(email);
            if (registeredUser != null) {
                registeredUser.setRoles(userMapper.getUserRoles(registeredUser.id));
            }
            return registeredUser;
        } catch (Exception ex) {
            throw new GetEntityException(MessageFormat.format("Cannot get user with email: {0}; Original message: {1}", email, ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public UserEntity getAnonymousUser(String sessionId) throws CreateEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            UserEntity user = new UserEntity(sessionId);
            userMapper.addAnonymousUser(user);
            dbSession.commit();
            return user;
        } catch (Exception ex) {
            throw new CreateEntityException(MessageFormat.format("Cannot create or get anonymous user: {0}; Original message: {1}", sessionId, ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public void mergeShoppingCarts(int authenticatedUserId, int anonymousUserId) throws UpdateEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            userMapper.mergeShoppingItems(anonymousUserId, authenticatedUserId);
            dbSession.commit();
        } catch (Exception ex) {
            throw new UpdateEntityException(MessageFormat.format("Cannot merge shopping carts; Original message: {0}", ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public Boolean validate(UserEntity userEntity) throws GetEntityException, IOException {
        return validate(userEntity.email, userEntity.password);
    }

    public void update(UserEntity user, UserEntity updatedUser) throws UpdateEntityException, IOException {
        boolean isModelChanged = false;
        if (!user.email.equals(updatedUser.email))
            isModelChanged = true;
        if (!user.phoneNumber.equals(updatedUser.phoneNumber))
            isModelChanged = true;
        if (!user.address.equals(updatedUser.address))
            isModelChanged = true;
        updatedUser.id = user.id;
        if (isModelChanged) {
            SqlSession dbSession = DBSession.openSession();
            try {
                UserMapper userMapper = dbSession.getMapper(UserMapper.class);
                userMapper.update(updatedUser);
                dbSession.commit();
            } catch (Exception ex) {
                throw new UpdateEntityException(MessageFormat.format("Cannot update user; Original message: {0}", ex.getMessage()));
            } finally {
                if (dbSession != null) {
                    dbSession.close();
                }
            }
        }
    }

    public boolean changePassword(String oldPassword, String newPassword, String username) throws UpdateEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            int result = userMapper.changePassword(username, oldPassword, newPassword);
            dbSession.commit();
            return result > 0;
        } catch (Exception ex) {
            throw new UpdateEntityException(MessageFormat.format("Cannot change password: {0}", ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public PaginatedList<UserEntity> getUserList(int pageNumber, int pageSize) throws GetEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            if (pageNumber == 0)
                pageNumber = 1;
            if (pageSize == 0)
                pageSize = 50;
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            List<UserEntity> users = userMapper.getUserList(pageNumber - 1, pageSize);
            int usersTotalCount = userMapper.getUserListTotalCount();
            return new PaginatedList<UserEntity>(users, usersTotalCount, pageSize);
        } catch (Exception ex) {
            throw new GetEntityException(MessageFormat.format("Cannot get users; Original message: {0}", ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public void delete(int id) throws DeleteEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            userMapper.deleteUser(id);
            dbSession.commit();
        } catch (Exception ex) {
            throw new DeleteEntityException(MessageFormat.format("Cannot delete user; Original message: {0}", ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public UserEntity getUser(int id) throws GetEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            UserEntity registeredUser = userMapper.getById(id);
            registeredUser.setRoles(userMapper.getUserRoles(registeredUser.id));
            return registeredUser;
        } catch (Exception ex) {
            throw new GetEntityException(MessageFormat.format("Cannot get user with id: {0}; Original message: {1}", id, ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public UserInformation getUserInfo(int id) throws GetEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            OrderMapper orderMapper = dbSession.getMapper(OrderMapper.class);
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            UserEntity user = userMapper.getById(id);
            List<OrderEntity> orders = orderMapper.getUserOrders(id);
            return new UserInformation(user, orders);
        } catch (Exception ex) {
            throw new GetEntityException(MessageFormat.format("Cannot get user information with id: {0}; Original message: {1}", id, ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public void edit(UserEntity user) {
        throw new NotImplementedException();
    }

    public void setIsAdmin(int id, boolean isAdmin) throws UpdateEntityException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            userMapper.setIsAdmin(id, isAdmin);
            dbSession.commit();
        } catch (Exception ex) {
            throw new UpdateEntityException(MessageFormat.format("Cannot update user with id: {0}; Original message: {1}", id, ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }

    public List<String> getAdminEmails() throws GetEntityListException, IOException {
        SqlSession dbSession = DBSession.openSession();
        try {
            UserMapper userMapper = dbSession.getMapper(UserMapper.class);
            return userMapper.getAdminEmails();
        } catch (Exception ex) {
            throw new GetEntityListException(MessageFormat.format("Cannot get admin emails; Original message: {0}", ex.getMessage()));
        } finally {
            if (dbSession != null) {
                dbSession.close();
            }
        }
    }
}
